using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Microsoft.SharedSource.SqlServer.MDDE.Middleware;
using System.Data.SqlClient;

namespace Microsoft.SharedSource.SqlServer.MDDE.ManagementStudio
{
    public partial class UpdateTemplate : Form
    {
        string repositoryConnectionString;
        string templateName;
        XmlDocument templateConfigurations;
        XmlElement rootElement;
        XmlDocument templateTree;
        bool templateConfigurationsLocked = false;

        public UpdateTemplate(
            string repositoryConnectionString, string templateName)
        {
            InitializeComponent();

            this.repositoryConnectionString = repositoryConnectionString;
            this.templateName = templateName;
        }

        private void UpdateTemplate_Load(object sender, EventArgs e)
        {
            // Fetch the template configurations information.
            OperationResult result =
                MDDEManager.GetTemplateConfigurableComponents(
                this.repositoryConnectionString, this.templateName);

            if (result.SuccessStatus == false)
            {
                MessageBox.Show(
                    "Error getting the template information. \n" +
                    result.OperationLog[0],
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                this.Close();

                return;
            }
            else
            {
                this.templateConfigurations =
                    (XmlDocument)result.OperationOutputParameters[0];

                this.rootElement =
                    (XmlElement)templateConfigurations.DocumentElement;
            }

            // Fetch the template tree.
            result =
                MDDEManager.GetTemplateTree(
                this.repositoryConnectionString, this.templateName);

            if (result.SuccessStatus == false)
            {
                MessageBox.Show(
                    "Error getting the template tree. \n" +
                    result.OperationLog[0],
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                this.Close();

                return;
            }
            else
                this.templateTree =
                    (XmlDocument)result.OperationOutputParameters[0];

            #region Display the template information.

            SqlConnectionStringBuilder cb =
                new SqlConnectionStringBuilder(
                this.repositoryConnectionString);

            lblRepository.Text =
                cb.DataSource + "." + cb.InitialCatalog;

            lblTemplate.Text = this.templateName;

            // Populate the template information.
            trvTemplateComponents.Nodes.Clear();
            lblComponentName.Text = string.Empty;
            rtbComponentURI.Text = string.Empty;
            dgProperties.Rows.Clear();

            TemplateComponentNode tn =
                new TemplateComponentNode(templateTree.DocumentElement);

            tn.PopulateSubtree();

            trvTemplateComponents.Nodes.Add(tn);

            #endregion
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            // Pass on the new template configurations 
            // to middleware for template updation.
            OperationResult result = 
                MDDEManager.UpdateTemplateConfigurations(
                repositoryConnectionString, 
                templateName, 
                templateConfigurations);

            if (result.SuccessStatus == false)
            {
                StringBuilder errorLog = new StringBuilder();
                foreach (string logEntry in result.OperationLog)
                    errorLog.Append(logEntry);

                MessageBox.Show("Template could not be updated." +
                    errorLog.ToString(), "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return;
            }
            else
            {
                MessageBox.Show("Template successfully updated.",
                    "Success", MessageBoxButtons.OK,
                    MessageBoxIcon.Information);

                this.Close();
            }
        }

        private void trvTemplateComponents_AfterSelect(
            object sender, TreeViewEventArgs e)
        {
            // Lock the configuration document.
            templateConfigurationsLocked = true;

            // Change the label texts.
            lblComponentName.Text = e.Node.Name;
            rtbComponentURI.Text = e.Node.FullPath;
            dgProperties.Rows.Clear();

            // Disable the mappings checkbox if the selected 
            // node is not of the type "Component".
            if (((XmlNode)e.Node.Tag).Attributes["Type"].Value.Equals(
                "Component", StringComparison.InvariantCultureIgnoreCase))
            {
                chkConfigurableMappings.Enabled = true;
                chkCollections.Enabled = true;
            }
            else
            {
                chkConfigurableMappings.Enabled = false;
                chkCollections.Enabled = false;
            }

            // Populate the grid and checkbox.
            XmlNode templateTreeNode = (XmlNode)e.Node.Tag;
            XmlNodeList propertyNodes =
                templateTreeNode.SelectNodes("./Property");

            // See if we have entry for this 
            // node in templateConfigurations.
            XmlNode configurationNode = templateConfigurations.
                SelectSingleNode(
                "//ConfigurableComponent[@ComponentUri=\"" +
                templateTreeNode.Attributes["Uri"].Value + "\"]");

            // Entry not found.
            if (configurationNode == null)
            {
                // Populate the grid with unchecked first column.
                foreach (XmlNode childNode in propertyNodes)
                    dgProperties.Rows.Add(null,
                        childNode.Attributes["Name"].Value);

                // Clear off the mappings checkbox. 
                chkConfigurableMappings.Checked = false;

                // Clear off the collections checkbox. 
                chkCollections.Checked = false;
            }
            // Entry found!
            else
            {
                XmlNode configurableProperty;
                // Populate the grid taking into account 
                // the earlier property values.
                foreach (XmlNode childNode in propertyNodes)
                {
                    // Check if we have this configurable 
                    // property already marked.
                    configurableProperty =
                        configurationNode.SelectSingleNode(
                        "./ConfigurableProperty[@Name=\"" +
                        childNode.Attributes["Name"].Value + "\"]");

                    if (configurableProperty == null)
                        dgProperties.Rows.Add(null,
                            childNode.Attributes["Name"].Value);
                    else
                        dgProperties.Rows.Add(true,
                            childNode.Attributes["Name"].Value);
                }

                // Populate the mappings checkbox with earlier 
                // mappings information.
                if (configurationNode.Attributes["ContainsMappings"].
                    Value == "True")
                    chkConfigurableMappings.Checked = true;
                else
                    chkConfigurableMappings.Checked = false;

                // Populate the collections checkbox with earlier 
                // collections information.
                if (configurationNode.Attributes["ContainsCollections"].
                    Value == "True")
                    chkCollections.Checked = true;
                else
                    chkCollections.Checked = false;
            }

            // Release the lock.
            templateConfigurationsLocked = false;
        }

        private void chkConfigurableMappings_CheckedChanged(
            object sender, EventArgs e)
        {
            // Do nothing if the configuration document is locked.
            if (templateConfigurationsLocked)
                return;

            // Lock the configurations document.
            templateConfigurationsLocked = true;

            // Get the configurations node for the selected component.
            XmlNode configurableComponent = GetSelectedComponent();

            // Update the mappings information.
            configurableComponent.Attributes["ContainsMappings"].
                Value = 
                chkConfigurableMappings.Checked ? "True" : "False";

            // Check and remove empty component nodes.
            RemoveEmptyConfigurableComponent(configurableComponent);

            // Release the document lock.
            templateConfigurationsLocked = false;
        }

        private void chkCollections_CheckedChanged(
            object sender, EventArgs e)
        {
            // Do nothing if the configuration document is locked.
            if (templateConfigurationsLocked)
                return;

            // Lock the configurations document.
            templateConfigurationsLocked = true;

            // Get the configurations node for the selected component.
            XmlNode configurableComponent = GetSelectedComponent();

            // Update the collections information.
            configurableComponent.Attributes["ContainsCollections"].
                Value = 
                chkCollections.Checked ? "True" : "False";

            // Check and remove empty component nodes.
            RemoveEmptyConfigurableComponent(configurableComponent);

            // Release the document lock.
            templateConfigurationsLocked = false;
        }

        private void dgProperties_CellValueChanged(
            object sender, DataGridViewCellEventArgs e)
        {
            // Do nothing if the configuration document is locked.
            if (templateConfigurationsLocked)
                return;

            if (e.RowIndex < 0 || e.RowIndex > dgProperties.Rows.Count - 1)
                return;

            // Lock the configurations document.
            templateConfigurationsLocked = true;

            // Get the configurations node for the selected component.
            XmlNode configurableComponent = GetSelectedComponent();

            // Add/Remove configurable property.
            string propertyName =
                dgProperties.Rows[e.RowIndex].
                Cells["propertyName"].Value.ToString();
            bool isConfigurable =
                (Boolean)dgProperties.Rows[e.RowIndex].
                Cells["isConfigurable"].Value;
            XmlNode configurableProperty;

            // Get the configurable property node.
            configurableProperty =
                configurableComponent.SelectSingleNode(
                "./ConfigurableProperty[@Name=\"" +
                propertyName + "\"]");

            // Add the configuration if the change 
            // is for inclusion of the property.
            if ((configurableProperty == null) && isConfigurable)
                AppendChildElement(
                    configurableComponent,
                    "ConfigurableProperty",
                    new string[] { 
                        "Name",propertyName
                        });

            // Remove the configuration if the change 
            // is for exclusion of the property.
            if ((configurableProperty != null) && !isConfigurable)
                configurableProperty.ParentNode.RemoveChild(
                    configurableProperty);

            // Check and remove empty component nodes.
            RemoveEmptyConfigurableComponent(configurableComponent);

            // Release the document lock.
            templateConfigurationsLocked = false;
        }

        private static void RemoveEmptyConfigurableComponent(
            XmlNode configurableComponent)
        {
            // If there are no configurable properties for this 
            // component, neither does it contain mappings,
            // nor it contains configurable column collections, remove it.
            if (!configurableComponent.HasChildNodes &&
                configurableComponent.Attributes["ContainsMappings"].
                Value == "False" &&
                configurableComponent.Attributes["ContainsCollections"].
                Value == "False")
                configurableComponent.ParentNode.RemoveChild(
                    configurableComponent);
        }

        private XmlNode GetSelectedComponent()
        {
            // Get the component node.
            XmlNode componentNode =
                (XmlNode)trvTemplateComponents.SelectedNode.Tag;

            // Pick out the component Uri.
            string componentUri =
                componentNode.Attributes["Uri"].Value;

            // See if we already have this component enlisted.
            XmlNode configurableComponent = rootElement.
                SelectSingleNode("//ConfigurableComponent[@ComponentUri=\"" +
                componentUri + "\"]");

            // Create a new node for this component 
            // if it does not exist already.
            if (configurableComponent == null)
                configurableComponent =
                    AppendChildElement(
                    rootElement,
                    "ConfigurableComponent",
                    new string[] {
                        "ComponentName", 
                        componentNode.Attributes["Name"].Value,
                        "ComponentClassId", 
                        componentNode.Attributes["ClassId"].Value,
                        "ComponentUri", 
                        componentNode.Attributes["Uri"].Value,
                        "ComponentType", 
                        componentNode.Attributes["Type"].Value,
                        "RowsetType", 
                        componentNode.Attributes["RowsetDescriptorType"].Value,
                        "ContainsMappings", 
                        "False",
                        "ContainsCollections", 
                        "False"
                    });

            return configurableComponent;
        }

        private XmlElement AppendChildElement(XmlNode parentElement,
            string childelementName, params string[] attributeFollowedByValue)
        {
            XmlElement childElement =
                parentElement.OwnerDocument.CreateElement(childelementName);

            parentElement.AppendChild(childElement);

            for (int i = 0; i < attributeFollowedByValue.Length; i++)
            {
                XmlAttribute attrib =
                    parentElement.OwnerDocument.
                    CreateAttribute(attributeFollowedByValue[i]);
                childElement.Attributes.Append(attrib);

                i++;

                attrib.Value =
                    (i >= attributeFollowedByValue.Length) ?
                    string.Empty : attributeFollowedByValue[i];
            }

            return childElement;
        }
    }
}